// #define FOR_TEST

#ifdef FOR_TEST

#include <iostream>
#include <glm/glm.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Model/ModelLoader.h"

int main(int argc, char** argv)
{
using namespace namespace_easy_car_ui;
    Model::Ptr model = ModelLoader::GetInstance()->LoadModel("./Models/suzanne.fbx");
    model->PrintInfo();
    
    return 0;
}

#else

#include <iostream>

#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_surface.h>

#include "Model/ModelLoader.h"
#include "Renderer/Vulkan/VulkanRenderer.h"

static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static SDL_Texture *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
static namespace_easy_car_ui::Model::Ptr model = nullptr;

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

using namespace namespace_easy_car_ui;

/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
    
    SDL_SetAppMetadata("Easy_Car_UI", "1.0", "com.eniac.easy_car_ui");

    if (!SDL_Init(SDL_INIT_VIDEO))
    {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    if (!SDL_CreateWindowAndRenderer("EasyCarUI", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_VULKAN, &window, &renderer))
    {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, WINDOW_WIDTH, WINDOW_HEIGHT);
    if (nullptr == texture)
    {
        SDL_Log("Couldn't create texture: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    try
    {
        VulkanRenderer::GetInstance().Initialize(true, WINDOW_WIDTH, WINDOW_HEIGHT);
        VulkanRenderer::GetInstance().PrintPhysicalDevices();
        VulkanRenderer::GetInstance().PrintSelectedPhysicalDevice();

        model = ModelLoader::GetInstance()->LoadModel("./Models/suzanne.fbx");
        VulkanRenderer::GetInstance().AddModel(model);

        // Node testNode;
        // testNode.m_vertes = {
        //     {{ -0.5f, -0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
        //     {{  0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
        //     {{  0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
        //     {{ -0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
        //     ,
        //     {{ -0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
        //     {{  0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
        //     {{  0.5f,  0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
        //     {{ -0.5f,  0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
        // };

        // testNode.m_indices = {
        //     0, 1, 2, 2, 3, 0,
        //     4, 5, 6, 6, 7, 4
        // };

        // VulkanRenderer::GetInstance().SetNode(testNode);
        // uint32_t pixels[WINDOW_WIDTH * WINDOW_HEIGHT] {0};
        // VulkanRenderer::GetInstance().RenderOneFrame(pixels, 0);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }

    return SDL_APP_CONTINUE;  /* carry on with the program! */
}

bool isMouseLeftButtonDown = false;
float lastX {0};
float lastY {0};
float offsetX {0};
float offsetY = {0};
float deltaYaw {0};
float deltaPitch {0};

/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
    if (event->type == SDL_EVENT_QUIT) {
        return SDL_APP_SUCCESS;  /* end the program, reporting success to the OS. */
    }

    if (event->type == SDL_EVENT_KEY_DOWN)
    {
        if (event->key.key == SDLK_W)
        {
            VulkanRenderer::GetInstance().m_camera.m_isForward = true;
        }
        else if (event->key.key == SDLK_S)
        {
            VulkanRenderer::GetInstance().m_camera.m_isRetreated = true;
        }
        else if (event->key.key == SDLK_A)
        {
            VulkanRenderer::GetInstance().m_camera.m_isLeftwards = true;
        }
        else if (event->key.key == SDLK_D)
        {
            VulkanRenderer::GetInstance().m_camera.m_isRightwards = true;
        }
    }
    
    if (event->type == SDL_EVENT_KEY_UP)
    {
        if (event->key.key == SDLK_W)
        {
            VulkanRenderer::GetInstance().m_camera.m_isForward = false;
        }
        else if (event->key.key == SDLK_S)
        {
            VulkanRenderer::GetInstance().m_camera.m_isRetreated = false;
        }
        else if (event->key.key == SDLK_A)
        {
            VulkanRenderer::GetInstance().m_camera.m_isLeftwards = false;
        }
        else if (event->key.key == SDLK_D)
        {
            VulkanRenderer::GetInstance().m_camera.m_isRightwards = false;
        }
    }

    if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
    {
        if (event->button.button == SDL_BUTTON_LEFT)
        {
            isMouseLeftButtonDown = true;
            lastX = event->motion.x;
            lastY = event->motion.y;
            deltaYaw = 0;
            deltaPitch = 0;
        }
    }

    if (event->type == SDL_EVENT_MOUSE_BUTTON_UP)
    {
        if (event->button.button == SDL_BUTTON_LEFT)
        {
            isMouseLeftButtonDown = false;
            VulkanRenderer::GetInstance().m_camera.CommitFace(deltaYaw, deltaPitch);
        }
    }

    if (event->type == SDL_EVENT_MOUSE_MOTION)
    {
        if (true == isMouseLeftButtonDown)
        {
            offsetX = event->motion.x - lastX;
            offsetY = lastY - event->motion.y;

            deltaYaw = offsetX * 0.1;
            deltaPitch = offsetY * 0.1;


            VulkanRenderer::GetInstance().m_camera.UpdateFace(deltaYaw, deltaPitch);
        }
    }

    return SDL_APP_CONTINUE;  /* carry on with the program! */
}

/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate)
{
    using namespace namespace_easy_car_ui;

    const Uint64 now = SDL_GetTicks();

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);  /* black, full alpha */
    SDL_RenderClear(renderer);  /* start with a blank canvas. */

    uint32_t pixels[WINDOW_WIDTH * WINDOW_HEIGHT] {0};
    VulkanRenderer::GetInstance().RenderOneFrame(pixels, 0);
    if (!SDL_UpdateTexture(texture, nullptr, pixels, (WINDOW_WIDTH*4)))
    {
        SDL_Log("Couldn't update texture: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    if (!SDL_RenderTextureRotated(renderer, texture, nullptr, nullptr, 0, NULL, SDL_FLIP_VERTICAL))
    {
        SDL_Log("Couldn't render texture: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }

    // if (!SDL_RenderTexture(renderer, texture, nullptr, nullptr))
    // {
    //     SDL_Log("Couldn't render texture: %s", SDL_GetError());
    //     return SDL_APP_FAILURE;
    // }

    if (!SDL_RenderPresent(renderer))
    {
        SDL_Log("Couldn't render present: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    } /* put it all on the screen! */

    return SDL_APP_CONTINUE;  /* carry on with the program! */
}

/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
    VulkanRenderer::GetInstance().Deinitialize();
    SDL_DestroyTexture(texture);
    /* SDL will clean up the window/renderer for us. */
}


#endif

